package com.csx.micrometer.threadpool.core;

import com.csx.micrometer.common.util.MetricsUtil;
import com.csx.micrometer.threadpool.cache.ThreadPoolSpecialProCache;
import com.csx.micrometer.threadpool.constant.ThreadPoolMetricsConstant;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.ToDoubleFunction;

/**
 * @author cuisongxu
 * @date 2022/11/20 19:34
 */
public class ThreadPoolMetricBinder implements MeterBinder {

    private Map<String, ThreadPoolExecutor> threadPoolExecutors = new HashMap<>();

    public ThreadPoolMetricBinder(Map<String, ThreadPoolExecutor> threadPoolExecutors) {
        this.threadPoolExecutors = threadPoolExecutors;
    }

    @Override
    public void bindTo(MeterRegistry registry) {

        for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolExecutors.entrySet()) {
            String threadPoolName = entry.getKey();
            ThreadPoolExecutor threadPoolExecutor = entry.getValue();

            Tag tag = Tag.of("threadPoolName", threadPoolName);
            Tags tags = Tags.of(tag);

            // 获取当前线程池的创建时间
            ThreadPoolSpecialProCache.ThreadPoolPro threadPoolPro = ThreadPoolSpecialProCache.getInstance().getMap().get(threadPoolName);
            long threadPoolStartMillsTime = threadPoolPro.getThreadPoolStartMillsTime();

            // 定义获取线程池参数的回调
            ToDoubleFunction<ThreadPoolExecutor> getActiveCountFunction = ThreadPoolExecutor::getActiveCount;
            ToDoubleFunction<ThreadPoolExecutor> getCorePoolSizeFunction = ThreadPoolExecutor::getCorePoolSize;
            ToDoubleFunction<ThreadPoolExecutor> getLargestPoolSizeFunction = ThreadPoolExecutor::getLargestPoolSize;
            ToDoubleFunction<ThreadPoolExecutor> getPoolSizeFunction = ThreadPoolExecutor::getPoolSize;
            ToDoubleFunction<ThreadPoolExecutor> getQueueSizeFunction = value -> value.getQueue().size();
            ToDoubleFunction<ThreadPoolExecutor> getCompletedTaskCountFunction = ThreadPoolExecutor::getCompletedTaskCount;
            ToDoubleFunction<ThreadPoolExecutor> getTaskCountFunction = ThreadPoolExecutor::getTaskCount;
            ToDoubleFunction<ThreadPoolExecutor> getThreadPoolAvgThroughputFunction = value -> {

                long currentTimeMillis = System.currentTimeMillis();
                long completedTaskCount = value.getCompletedTaskCount();
                return (double) (completedTaskCount / ((currentTimeMillis - threadPoolStartMillsTime) / 1000));
            };

            ToDoubleFunction<ThreadPoolExecutor> getThreadPoolThroughputFunction = new ToDoubleFunction<ThreadPoolExecutor>() {
                @Override
                public double applyAsDouble(ThreadPoolExecutor value) {
                    long lastThreadPoolCompletedTaskCount = threadPoolPro.getLastThreadPoolCompletedTaskCount();
                    long lastComputeCompletedTaskMillsTime = threadPoolPro.getLastComputeCompletedTaskMillsTime();
                    long currentTimeMillis = System.currentTimeMillis();
                    long currentCompletedTaskCount = value.getCompletedTaskCount();

                    // 更新缓存
                    threadPoolPro.setLastComputeCompletedTaskMillsTime(currentTimeMillis);
                    threadPoolPro.setLastThreadPoolCompletedTaskCount(currentCompletedTaskCount);

                    return (double) ((currentCompletedTaskCount - lastThreadPoolCompletedTaskCount) / ((currentTimeMillis - lastComputeCompletedTaskMillsTime) / 1000));
                }
            };

            // 指标采集
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_ACTIVE_NUMBER, threadPoolExecutor,tags, registry, getActiveCountFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_CORE_THREAD_NUMBER, threadPoolExecutor, tags, registry, getCorePoolSizeFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_CURRENT_THREAD_NUMBER, threadPoolExecutor, tags, registry, getPoolSizeFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_MAXIMUM_THREAD_NUMBER, threadPoolExecutor, tags, registry, getLargestPoolSizeFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_EXECUTED_TASK_NUMBER, threadPoolExecutor, tags, registry, getCompletedTaskCountFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_TASK_NUMBER_IN_QUEUE, threadPoolExecutor, tags, registry, getQueueSizeFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_ALL_TASK_NUMBER, threadPoolExecutor, tags, registry,getTaskCountFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_AVG_THROUGHPUT, threadPoolExecutor, tags, registry,getThreadPoolAvgThroughputFunction);
            MetricsUtil.gauge(ThreadPoolMetricsConstant.THREAD_POOL_CURRENT_THROUGHPUT, threadPoolExecutor, tags, registry,getThreadPoolThroughputFunction);
        }
    }
}
